Uber 指示灯:帮助乘客快速寻找车辆

Uber 指示灯:帮助乘客快速寻找车辆

Uber Beacon app screen

本文是 Uber 的客户端工程师团队讲述了如何开发最新版本司机端系列文章中的第七篇,该系列代号 Carbon ,是我们共享出行业务的核心。包括其它功能在内,Uber 司机端使得超过 300 万名司机可以查看费用、里程以及收益情况。2017 年我们结合司机的反馈开始对司机端进行重新设计,并在 2018 年 9 月份启动了该项目。

由于技术和物理因素,接驾对于乘客和司机来说都是一个特别难的痛点。技术方面,我们需要司乘两端达成相对的同步。物理方面,例如在一个夜间拥挤的街道,司机、乘客将彼此很难找寻到对方。对于平台来说,帮助乘客轻松辨识车辆是成功开启愉快行程的关键。

指示灯 是我们应用程序进入现实世界的一种扩展,这是一个给司乘 2 方提供直观颜色匹配的设备,乘客可以选择光谱中的任何颜色进行设置。在黑暗和暴风雨的夜晚,选择一种颜色照亮司机车辆的挡风玻璃,可以让你更快速方便的找寻到车辆。事实上推动这种体验的工程设计并不简单。接下来让我们深入了解一下如何利用我们的 APP 实现这种体验。

Animation showing how Uber Beacon works

乘客通过指示灯功能来选择一种颜色,然后将其传输给汽车上的指示灯设备,这样乘客就可以快速方便的知道哪辆车是来接他的。

足迹

指示灯的大部分功能是在我们司机端 APP 上使用。实现需要使用 2 组资源:Beacon SDK:负责通过蓝牙跟指示灯设备进行通信。RIBs架构 中的一系列功能模块利用 Beacon SDK 实现司机端的功能。

Beacon SDK

Beacon SDK 几乎实现了指示灯设备的所有用户体验功能,它利用蓝牙实现了司机端 APP 和指示灯设备之间连接跟数据沟通的功能。在设计 Beacon SDK 时,我们遵循了三个原理:

  • 跨平台
  • 应用独立性
  • 响应式接口

跨平台

因为 Uber 的移动端架构是跨平台的,同时支持 iOS 跟安卓设备,所以 Beacon SDK 的架构设计也必须是跨平台的。在跨平台方面之后,我们可以更快速的迭代并提供给其他团队更方便的接入方式。

应用独立性

SDK 开发最关键的原则是去掉 SDK 对应用的依赖。随着我们逐步开发新司机端 APP,这点显得尤其重要。SDK 的独立性使得我们在新旧司机端 APP 同时开发中可以通过迭代 SDK 的方式同步进行,避免中断。

响应式接口

为了兼容 Uber 移动端响应式编程架构,在设计 Beacon SDK 时,必须暴露可观察对象(Observable)。使用这个 SDK 的工程师可以直接通过观察反应的方式与指示灯进行交互。使用这个 SDK 的工程师可以直接通过观察反应的方式与指示灯进行交互。蓝牙 API 的使用复杂性被抽象掉了。

结论

我们设计的 Beacon SDK 具有一系列的管理器对象(Manager),其中每个管理器负责指示灯功能的一部分,例如连接、LED 控制、无线下载(OTA)更新和传感器。每组管理器负责与指示灯设备的某个功能互通,司机端 APP 控制这些的管理器来操作指示灯设备。

Tree diagram showing how the core Beacon layer in the driver app connect to Beacon

图 1:Beacon 被分解成多个逻辑管理器,管理器用来反映 Beacon 状态并提供特定功能。

管理器(Manager)定义了接口,SDK 提供了对应接口的实现。这些管理器封装了蓝牙 API 将命令从司机端传递到指示灯设备。

工作者(Workers)

由于 Beacon SDK 并没有提供专用的用户界面,因此几乎所有的 Beacon 功能都是通过 worker 提供的。Worker 是本质上是没有专用UI组件的交互者,因此逻辑与 APP 中的视图无关。在需要使用指定功能时,Carbon 将 worker 添加到 RIB 树中实现,当从 RIB 树中移除时,worker 被分离且被垃圾回收。

每个 worker 都依赖于一个或多个 Beacon SDK 管理器(Manager)。 管理器被添加给 RIB 里面的 Worker 对象。当一个 worker 被实例化时,worker 会添加由上游范围提供的管理器对象。通过这种方式,Beacon 的管理器可以在所有 worker 之间共享,并且 worker 不负责这些管理器的生命周期。

RIBs tree for driver app showing plugin points for Beacon

图 2:在 Carbon 的 RIB 树的这一小节中,Beacon 的 Workers 都在 Active 和 Online 的时候才会生效。管理器在 Active 时被添加依赖并提供给下游 Workers。

RIB 树让我们根据 worker 是否使用 Beacon 功能来决定是启用还是禁用插件点(plugin points)。如果禁用 Beacon,插件点则禁用;如果 worker 不被创建,那么就不会初始化 Beacon SDK 的管理器,从根本上消除了 Beacon 运行时的内存空间。

如果启用 Beacon 功能,我们需要遵循协议,worker 才会被添加到 RIB 树中。例如 Beacon Alert 管理器,它是在应用内提供例如“低电量”或“指示灯未找到”这种提示窗的管理器。想象一下当司机在家的时候打开 APP 查看他们的收入,此时司机的状态是离线状态,因为司机并没有开始接单;此时司机并没有使用指示灯的需求,因此不需要收到类似的弹窗。我们只会在司机在路上接乘客的时候(在线时)才会接入 Beacon Alert 管理器,因为这个时候提示功能才有意义。

颜色匹配

有了 Beacon 框架,我们可以在司机端 APP 中实现颜色匹配功能。

以下流程都必须严格依赖司机端已连接到指示灯设备且此时是在线状态。当司机在线时,Beacon 的功能 worker 会添加到 RIB 树上,才能使用司机端的 Beacon 功能更新服务器。随着司机端的在线与离线,我们会相应的更新状态。当司机端断线时,后端会自动关闭该功能。

当司机端接到乘客的订单后,乘客端的 APP 就会收到司机的名字、车辆信息以及他们是否具有指示灯的能力。如果具有指示灯功能,我们会在乘客端 APP 中展示指示灯的图标。乘客端 APP 会自动给服务器发送之前选择过的颜色(如乘客从未选择颜色,则随机选择一种颜色),然后通知给司机端 APP 所选颜色。

在等待接驾时,乘客可以通过点击指示灯图标进入颜色选择页面(某些特定颜色因为安全合规被移除,例如红色、蓝色),在这里乘客可以选择任何他们中意的颜色。每当乘客选中了一个颜色,就会把这个色值传递给后端,后端又通知给司机端 APP。

Diagram of data flow between rider app, backend, and driver app

图 3:当司机端与指示灯连接成功后,就会被通知给后端,当乘客匹配到这个车辆,乘客就可以选择一种指示灯颜色。

在司机端这边,当司机在线时,有个 Beacon Color Worker 一直在监听颜色变化的通知。当收到了一个新颜色,就会本地存储一份。这样,如果司机接受了多个调度,例如拼车场景,我们会记录每个被接驾的乘客所设置的颜色。

Diagram showing data flow from multiple rider apps to backend

图 4:行程中有很多个拼车乘客,我们会存储这些乘客每个选择的颜色。

当后端通知我们已经在去接某个乘客路上时,我们会给 Beacon 控制颜色的管理器发送一条指令(-通过技术手段获得所匹配乘客选择的色值),告诉他显示这个乘客所选中的颜色。同时在接驾过程中,车辆会不断更新显示乘客之后更改的任何颜色,保持乘客端跟司机端的同步。

Diagram showing data flow from backend to driver app to Beacon

图 5:当乘客被接驾时,我们会检索他们所选择的最后一种颜色。

软硬件的结合

优步一直在寻找优化用户体验的方法。软硬件结合的方式将平台能力扩展到现实世界是一种自然的进步。软硬件结合的方式将平台能力扩展到现实世界带来了新的令人兴奋的挑战,优步指示灯只是如何在这个领域进行创新的一个例子。这也是优步的移动工程师最激动人心的时刻。

Index of articles in Uber driver app series

  1. 为什么我们决定重构 Uber 司机端
  2. 使用RIBs重构Uber司机端
  3. 新 Uber 司机端是如何克服网络延迟问题的
  4. Scaling Cash Payments in Uber Eats
  5. How to Ship an App Rewrite Without Risking Your Entire Business
  6. Building a Scalable and Reliable Map Interface for Drivers
  7. Uber指示灯:帮助乘客快速寻找车辆